﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Linq;
using System.ComponentModel;

namespace LogicProtocolAnalyzer
{
    internal delegate void ZoomFactor(int zoomFactor);

    public partial class AnalyzerControl : UserControl
    {
        internal const int NumberOfChannels = 8;

        internal event ZoomFactor ZoomFactorHandler;
        
        private int _zoom = 1;
        private SampledData _sampleData;

        private const int ChannelMarge = 10;
        private const int ChannelHeight = 50;

        internal SampledData SampleData
        {
            get { return _sampleData; }
            set
            {
                _sampleData = value;
                SetAutoScrollMinSize();
            }
        }

        internal Pen Pen { get; set; }

        public AnalyzerControl()
        {
            InitializeComponent();

            this.Pen = new Pen(Color.Black);
        }

        internal void Zoom(int zoom)
        {
            _zoom = zoom;

            SetAutoScrollMinSize();
            Invalidate();

            if (ZoomFactorHandler != null)
            {
                ZoomFactorHandler(_zoom);
            }
        }

        internal void ZoomIn()
        {
            if (_zoom == 8) return;

            _zoom *= 2;
            SetAutoScrollMinSize();
            Invalidate();

            if(ZoomFactorHandler != null)
            {
                ZoomFactorHandler(_zoom);
            }
        }

        internal void ZoomOut()
        {
            if (_zoom == 1) return;

            _zoom /= 2;
            SetAutoScrollMinSize();
            Invalidate();

            if (ZoomFactorHandler != null)
            {
                ZoomFactorHandler(_zoom);
            }
        }

        internal void SetAutoScrollMinSize()
        {
            if (_sampleData == null) return;

            this.AutoScrollMinSize = new Size(_sampleData.Length * _zoom, NumberOfChannels * (ChannelMarge + ChannelHeight) + ChannelMarge);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            if (SampleData == null || SampleData.Length == 0)
            {
                base.OnPaint(e);
                return;
            }

            Size scrollOffset = new Size(this.AutoScrollPosition);

            const int YInitValue = -1;
            Graphics dc = e.Graphics;

            List<List<Point>> channels = new List<List<Point>>();

            int x = 0;
            int[] lastY = new int[NumberOfChannels];
            for (int i = 0; i < NumberOfChannels; i++)
            {
                lastY[i] = YInitValue;
                channels.Add(new List<Point>());
            }

            int sampleDataLength = SampleData.Length;
            for (int dataIndex = 0; dataIndex < sampleDataLength; dataIndex++)
            {
                for (int channelIndex = 0; channelIndex < 8; channelIndex++)
                {
                    int channelFilter = 0x80 >> channelIndex;

                    int y = 0;
                    y = (((SampleData[dataIndex] & channelFilter) != 0) ? 0 : ChannelHeight) + (channelIndex + 1) * ChannelMarge + channelIndex * ChannelHeight;

                    if (lastY[channelIndex] != y || dataIndex == sampleDataLength - 1)
                    {
                        if (lastY[channelIndex] != YInitValue)
                        {
                            channels[channelIndex].Add(new Point(x, lastY[channelIndex]) + scrollOffset);
                        }
                        channels[channelIndex].Add(new Point(x, y) + scrollOffset);
                        lastY[channelIndex] = y;
                    }
                }
                x += _zoom;
            }

            foreach (List<Point> channel in channels)
            {
                dc.DrawLines(Pen, channel.ToArray());
            }

            base.OnPaint(e);
        }

        internal double GetTime(int xPosition)
        {
            if (SampleData == null || SampleData.SampleFrequency == SampleFrequency.Unknown || SampleData.SampleFrequency.AsFrequency() == 0) return 0;

            return (((xPosition + Math.Abs(this.AutoScrollPosition.X))) / _zoom) * (1 / SampleData.SampleFrequency.AsFrequency());
        }

        internal string Settings
        {
            get { return (_sampleData == null) ? string.Empty : _sampleData.Settings; }
        }

        internal void LoadSampleData(Stream stream)
        {
            _sampleData = new SampledData();
            _sampleData.Load(stream);
            SetAutoScrollMinSize();
            Invalidate();
        }

        internal void Acquire(BackgroundWorker backgroundWorker, DoWorkEventArgs doWorkEventArgs)
        {
            _sampleData = new SampledData();
            _sampleData.Acquire(backgroundWorker, doWorkEventArgs);
        }

        internal void Save(Stream stream)
        {
            _sampleData.Save(stream);
        }
    }
}
